#include <stdlib.h>
#include <string.h>
#include "CIpodLog.h"
#include "CIpodEvent.h"
#include "CIpodConfig.h"
#include "Iap2Def.h"

IPODObserver *CIpodEvent::s_Observer = NULL;

void CIpodEvent::registerObserver(IPODObserver *observer) {
    s_Observer = observer;
}

void CIpodEvent::unregisterObserver() {
    s_Observer = NULL;
}
int CIpodEvent::read(ipod_t handle, uint8 *data, uint16 len,int* length){
    if (s_Observer != NULL && s_Observer->on_request_accessory_read != NULL) {
//        CIpodLog::d("CIpodEvent::read, Call Observer->on_request_accessory_read. \n");
        return s_Observer->on_request_accessory_read(handle, data,len,length);
    }
	return -1;
}
int CIpodEvent::write(ipod_t handle, uint8 *data, uint16 len,int* length){
    if (s_Observer != NULL && s_Observer->on_request_accessory_write != NULL) {
//        CIpodLog::d("CIpodEvent::write, Call Observer->on_request_accessory_write. \n");
        return s_Observer->on_request_accessory_write(handle, data,len,length);
    }
	return -1;
}

int CIpodEvent::accessoryVidPid(ipod_t handle, void *vid, void *pid){
    if (s_Observer != NULL && s_Observer->on_read_accessory_vid_pid != NULL
    		&& CIpodConfig::getHidg() == 0) {
        s_Observer->on_read_accessory_vid_pid(handle, vid, pid);
        return 0;
    }
	return -1;
}

int CIpodEvent::reportCallEvent(ipod_t handle, int keyCode,int keyAction){
    if (s_Observer != NULL && s_Observer->on_call_event != NULL
    		&& CIpodConfig::getHidg() == 0) {
        s_Observer->on_call_event(handle, keyCode, keyAction);
        return 0;
    }
	return -1;
}

int CIpodEvent::startHID(ipod_t handle, uint8 *data, uint16 len){
    if (s_Observer != NULL && s_Observer->on_accessory_start_hid != NULL
    		&& CIpodConfig::getHidg() == 0) {
        s_Observer->on_accessory_start_hid(handle, data,len);
        return 0;
    }
	return -1;
}

int CIpodEvent::hidReport(ipod_t handle, uint8 *data, uint16 len){
    if (s_Observer != NULL && s_Observer->on_accessory_hid_report != NULL ) {
       s_Observer->on_accessory_hid_report(handle, data,len);
       return 0;
    }
	return -1;
}

int CIpodEvent::wifiConfigurationInformation(ipod_t handle, void *data){
    if (s_Observer != NULL && s_Observer->on_accessory_wifi_info != NULL ) {
       s_Observer->on_accessory_wifi_info(handle, data);
       return 0;
    }
	return -1;
}

int CIpodEvent::httpInfoReport(ipod_t handle, void *ipAddress, int port,void * srcVersion,void * deviceID,void * publicKey) {
    if (s_Observer != NULL && s_Observer->on_accessory_hid_report != NULL ) {
       s_Observer->on_http_info_report(handle, ipAddress,port,srcVersion,deviceID,publicKey);
       return 0;
    }
	return -1;
}

int CIpodEvent::publish_shark_bonjour(){
    if (s_Observer != NULL && s_Observer->on_request_publish_shark_bonjour != NULL) {
        return s_Observer->on_request_publish_shark_bonjour();
    }
	return -1;
}

int CIpodEvent::identificationInfo(){
    if (s_Observer != NULL && s_Observer->on_identification_info != NULL) {
        return s_Observer->on_identification_info();
    }
	return -1;
}

int CIpodEvent::request_shark_deviceId(uint8 *data, uint16 len){
    if (s_Observer != NULL && s_Observer->on_request_shark_deviceId != NULL) {
        return s_Observer->on_request_shark_deviceId(data,len);
    }
	return -1;
}


void CIpodEvent::sendEvent(IPODEventID id, ipod_t handle) {
    sendEvent(id, handle, -1, NULL);
}

void CIpodEvent::sendEvent(IPODEventID id, ipod_t handle, int param) {
    sendEvent(id, handle, param, NULL);
}

void CIpodEvent::sendEvent(IPODEventID id, ipod_t handle, const void *obj) {
    sendEvent(id, handle, -1, obj);
}

void CIpodEvent::sendEvent(IPODEventID id, ipod_t handle, int param, const void *obj) {
    switch (id) {
    case IPOD_EVT_CONNECT_STATUS_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_CONNECT_STATUS_CHANGED, handle = %d, status = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_connect_status_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_connect_status_changed. \n");
            s_Observer->on_connect_status_changed(handle, param);
        }
        break;

    case IPOD_EVT_A2DP_STATUS_CHANGED:
        CIpodLog::i("CIpodEvent::sendEvent, IPOD_EVT_A2DP_STATUS_CHANGED, handle = %d, status = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_a2dp_status_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_a2dp_status_changed. \n");
            s_Observer->on_a2dp_status_changed(handle, (bool) param);
        }
        break;

    case IPOD_EVT_SAMPLE_RATE_CHANGED:
        CIpodLog::i("CIpodEvent::sendEvent, IPOD_EVT_SAMPLE_RATE_CHANGED, handle = %d, rate = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_sample_rate_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_sample_rate_changed. \n");
            s_Observer->on_sample_rate_changed(handle, param);
        }
        break;

    case IPOD_EVT_TRACK_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_TRACK_CHANGED, handle = %d, index = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_track_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_track_changed. \n");
            s_Observer->on_track_changed(handle, param);
        }
        break;

    case IPOD_EVT_TRACK_POSITION_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_TRACK_POSITION_CHANGED, handle = %d, position = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_track_position_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_track_position_changed. \n");
            s_Observer->on_track_position_changed(handle, (uint32) param);
        }
        break;

    case IPOD_EVT_TRACK_DURATION_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_TRACK_DURATION_CHANGED, handle = %d, duration = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_track_duration_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_track_duration_changed. \n");
            s_Observer->on_track_duration_changed(handle, (uint32) param);
        }
        break;

    case IPOD_EVT_SHUFFLE_MODE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_SHUFFLE_MODE_CHANGED, handle = %d, mode = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_shuffle_mode_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_shuffle_mode_changed. \n");
            s_Observer->on_shuffle_mode_changed(handle, param);
        }
        break;

    case IPOD_EVT_REPEAT_MODE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_REPEAT_MODE_CHANGED, handle = %d, mode = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_repeat_mode_changed != NULL)  {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_repeat_mode_changed. \n");
            s_Observer->on_repeat_mode_changed(handle, param);
        }
        break;

    case IPOD_EVT_PLAYBACK_STATE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_PLAYBACK_STATE_CHANGED, handle = %d, status = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_playback_state_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_playback_state_changed. \n");
            s_Observer->on_playback_state_changed(handle, param);
        }
        break;

    case IPOD_EVT_ALBUM_ARTWORK_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_ALBUM_ARTWORK_CHANGED, handle = %d, path = %s\n", handle, (char *) obj);
        if (s_Observer != NULL && s_Observer->on_album_artwark_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_album_artwark_changed. \n");
            s_Observer->on_album_artwark_changed(handle, (char *) obj);
        }
        break;

    case IPOD_EVT_FILE_MUSIC_RADIO_PATH:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_FILE_MUSIC_RADIO_PATH, handle = %d, path = %s\n", handle, (char *) obj);
        if (s_Observer != NULL && s_Observer->on_file_music_radio_path != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_file_music_radio_path. \n");
            s_Observer->on_file_music_radio_path(handle, (char *) obj);
        }
        break;

    case IPOD_EVT_FILE_MUSIC_LOCAL_DEVICE_PATH:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_FILE_MUSIC_LOCAL_DEVICE_PATH, handle = %d, path = %s\n", handle, (char *) obj);
        if (s_Observer != NULL && s_Observer->on_file_music_local_device_path != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_file_music_local_device_path. \n");
            s_Observer->on_file_music_local_device_path(handle, (char *) obj);
        }
        break;

    case IPOD_EVT_MEDIA_UPDATE_PROGRESS:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_MEDIA_UPDATE_PROGRESS, handle = %d, progress = %d\n", handle, param);
        if (s_Observer != NULL && s_Observer->on_media_update_progress != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_media_update_progress. \n");
            s_Observer->on_media_update_progress(handle, (uint8) param);
        }
        break;

    case IPOD_EVT_EA_DATA_RECEIVED:
        if (s_Observer != NULL && s_Observer->on_ea_data_received != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_ea_data_received. \n");
            s_Observer->on_ea_data_received((uint8 *) obj, (uint16) param);
        }
        break;

    case IPOD_EVT_EA_NATIVE_STATE_CHANGED:
        if (s_Observer != NULL && s_Observer->on_ea_native_state_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_ea_native_state_changed. \n");
            s_Observer->on_ea_native_state_changed(param);
        }
        break;

	case IPOD_EVT_DEVICE_INFORMATION_UPDATE:
		CIpodLog::d("Events IPOD_EVT_DEVICE_INFORMATION_UPDATE handle: %d, path: %s.\n", handle, (char *) obj);
		if (s_Observer != NULL && s_Observer->on_device_information_changed != NULL)
        {
			CIpodLog::d("Events IPOD_EVT_DEVICE_INFORMATION_UPDATE Point is not NULL.\n");
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_device_information_changed. \n");
            s_Observer->on_device_information_changed(handle, (char *) obj);
        }
		break;
    case IPOD_EVT_CALL_STATUS_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_CALL_STATUS_CHANGED \n");
        if (s_Observer != NULL && s_Observer->on_call_state_changed != NULL)
        {
            IAP2CallStatusInfo   *m_CallStatus = (IAP2CallStatusInfo *) obj;
            CallStateInformation temp_callstatus;
            int remoteID_len = m_CallStatus->remoteID.size();
            if(remoteID_len > IPOD_TRACK_NAME_MAX_LEN)
            {
                remoteID_len = IPOD_TRACK_NAME_MAX_LEN -1;
                temp_callstatus.call_state_phone_number[IPOD_TRACK_NAME_MAX_LEN -1] ='\0';
            }
            memcpy(temp_callstatus.call_state_phone_number,m_CallStatus->remoteID.c_str(),remoteID_len);
            temp_callstatus.call_state_phone_number[remoteID_len] ='\0';

            int displayName_len = m_CallStatus->displayName.size();
            if(displayName_len > IPOD_TRACK_NAME_MAX_LEN){
                displayName_len = IPOD_TRACK_NAME_MAX_LEN -1;
                temp_callstatus.call_state_network_sent_caller_name[IPOD_TRACK_NAME_MAX_LEN -1] ='\0';
            }
            memcpy(temp_callstatus.call_state_network_sent_caller_name,m_CallStatus->displayName.c_str(),displayName_len);
            temp_callstatus.call_state_network_sent_caller_name[displayName_len] ='\0';

            temp_callstatus.call_state_status = m_CallStatus->status;
            temp_callstatus.call_state_direction = m_CallStatus->direction;

            int callUUID_len = m_CallStatus->callUUID.size();
            if(callUUID_len > IPOD_TRACK_NAME_MAX_LEN){
                callUUID_len = IPOD_TRACK_NAME_MAX_LEN-1;
                temp_callstatus.unique_call_id[IPOD_TRACK_NAME_MAX_LEN -1] ='\0';
            }
            memcpy(temp_callstatus.unique_call_id,m_CallStatus->callUUID.c_str(),callUUID_len);
            temp_callstatus.unique_call_id[callUUID_len] ='\0';

            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_call_state_changed. \n");
            s_Observer->on_call_state_changed(handle, &temp_callstatus);
        }
        break;
    case IPOD_EVT_LOCATION_ENABLE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_LOCATION_ENABLE_CHANGED, handle = %d\n", handle);
        if (s_Observer != NULL && s_Observer->on_location_enable_changed != NULL) {
             CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_location_enable_changed. \n");
            if(param == 1){
                s_Observer->on_location_enable_changed(handle, param,(CIAP2LocationType *) obj);
            }else{
                s_Observer->on_location_enable_changed(handle, param,NULL);
            }
        }
        break;
    case IPOD_EVT_DEVICE_TRANSPORTID_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_DEVICE_TRANSPORTID_CHANGED, handle = %d\n", handle);
        if (s_Observer != NULL && s_Observer->on_device_transportid_changed != NULL) {
            CDeviceTransportNotification temp_deviceid;
            DeviceTransportNotification *deviceid = (DeviceTransportNotification *) obj;
            int BluetoothTransportIdentifier_len = deviceid->BluetoothTransportIdentifier.size();
            memset(temp_deviceid.BluetoothTransportIdentifier,'\0',sizeof(temp_deviceid.BluetoothTransportIdentifier));
            if(BluetoothTransportIdentifier_len > IPOD_TRACK_NAME_MAX_LEN){
                BluetoothTransportIdentifier_len = IPOD_TRACK_NAME_MAX_LEN -1;
                temp_deviceid.BluetoothTransportIdentifier[IPOD_TRACK_NAME_MAX_LEN -1] ='\0';
            }
            memcpy(temp_deviceid.BluetoothTransportIdentifier,deviceid->BluetoothTransportIdentifier.c_str(),BluetoothTransportIdentifier_len);

            int USBTransportIdentifier_len = deviceid->USBTransportIdentifier.size();
            memset(temp_deviceid.USBTransportIdentifier,'\0',sizeof(temp_deviceid.USBTransportIdentifier));
            if(USBTransportIdentifier_len > IPOD_TRACK_NAME_MAX_LEN){
                USBTransportIdentifier_len = IPOD_TRACK_NAME_MAX_LEN -1;
                temp_deviceid.USBTransportIdentifier[IPOD_TRACK_NAME_MAX_LEN -1] ='\0';
            }
            memcpy(temp_deviceid.USBTransportIdentifier,deviceid->USBTransportIdentifier.c_str(),USBTransportIdentifier_len);

            CIpodLog::d("CIpodEvent::sendEvent,USBTransportIdentifier=%s, deviceid = %s\n", temp_deviceid.USBTransportIdentifier,deviceid->USBTransportIdentifier.c_str());
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_device_transportid_changed. \n");
            s_Observer->on_device_transportid_changed(handle,&temp_deviceid );
        }
        break;
    case IPOD_EVT_WIRELESS_CARPLAY_AVAILABLE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_WIRELESS_CARPLAY_AVAILABLE_CHANGED, handle = %d param:%d \n", handle,param);
        if (s_Observer != NULL && s_Observer->on_wireless_carplay_update != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_wireless_carplay_update. \n");
            s_Observer->on_wireless_carplay_update(handle, param);
        }
        break;
    case IPOD_EVT_ROUTE_GUIDANCE_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_ROUTE_GUIDANCE_CHANGED, handle = %d \n", handle);
        if (s_Observer != NULL && s_Observer->on_route_guidance_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_route_guidance_changed. \n");
            s_Observer->on_route_guidance_changed(handle, (RouteGuidanceUpdate *) obj);
        }
        break;
    case IPOD_EVT_ROUTE_GUIDANCE_MANEUVER_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_ROUTE_GUIDANCE_MANEUVER_CHANGED, handle = %d\n", handle);
        if (s_Observer != NULL && s_Observer->on_route_guidance_maneuver_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_route_guidance_maneuver_changed. \n");
            s_Observer->on_route_guidance_maneuver_changed(handle, (RouteGuidanceManeuverUpdate *) obj);
        }
        break;
    case IPOD_EVT_DEVICE_MAC_ADDRESS:
        CIpodLog::i("CIpodEvent::sendEvent, IPOD_EVT_DEVICE_MAC_ADDRESS, handle = %d, address = %s\n", handle, (char *) obj);
        if (s_Observer != NULL && s_Observer->on_device_mac_address != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_device_mac_address. \n");
            s_Observer->on_device_mac_address(handle, (char *) obj);
        }
        break;
    case IPOD_EVT_DEVICE_LINK_KEY_INFORMATION:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_DEVICE_LINK_KEY_INFORMATION, handle = %d\n", handle);
        if (s_Observer != NULL && s_Observer->on_device_link_key != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_device_link_key. \n");
            s_Observer->on_device_link_key(handle, (OOBLinkKeyInformation *) obj);
        }
        break;
    case IPOD_EVT_CARPLAY_AVAILABILITY_INFORMATION:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_CARPLAY_AVAILABILITY_INFORMATION, handle = %d\n", handle);
        if (s_Observer != NULL && s_Observer->on_carplay_availability != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_carplay_availability. \n");
            s_Observer->on_carplay_availability(handle, (CarPlayAvailInfo *) obj);
        }
        break;
    case IPOD_EVT_MEDIA_PLAYING_LIST_CHANGED:
        CIpodLog::i("CIpodEvent::sendEvent, IPOD_EVT_MEDIA_PLAYING_LIST_CHANGED, handle = %d param:%d\n", handle,param);
        if (s_Observer != NULL && s_Observer->on_media_playing_list_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_media_playing_list_changed. \n");
            s_Observer->on_media_playing_list_changed(handle,param);
        }
        break;
    case IPOD_EVT_PLAYBACK_QUEUELIST_AVAILABLE:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_PLAYBACK_QUEUELIST_AVAILABLE, handle = %d param:%d\n", handle,param);
        if (s_Observer != NULL && s_Observer->on_playback_queuelist_available != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_playback_queuelist_available. \n");
            s_Observer->on_playback_queuelist_available(handle,(bool) param);
        }
        break;
    case IPOD_EVT_LOCATION_INFO_CHANGED:
        CIpodLog::d("CIpodEvent::sendEvent, IPOD_EVT_LOCATION_INFO_CHANGED, handle = %d param:%d\n", handle,param);
        if (s_Observer != NULL && s_Observer->on_location_information_changed != NULL) {
            CIpodLog::d("CIpodEvent::sendEvent, Call Observer->on_location_information_changed. \n");
            s_Observer->on_location_information_changed((char *)obj);
        }
        break;
    }
}
